/*
 *  PlayState.cpp
 *
 *  Created by Jason Townes French on 4/30/08.
 *
 */
#include <Ogre.h>

#include "PlayState.h"

using namespace Ogre;

void PlayState::enter(Root* r)
{
	//Store pointer to Root node
	mRoot = r;
	
	/*
	Build Level Modules/Objects
	*/
	
	//Initialize sound engine
	mSoundEngine = new SoundEngine();
	mSoundEngine->init();
	mSoundEngine->load3DSound("/Users/jfrench/Music/iTunes/iTunes Music/311/311/test.mp3");
	mSoundEngine->load3DSound("/Developer/SDKs/OgreSDK/Sounds/bang_6.wav");
	
	//Initialize physics engine
	mWorld = new OgreNewt::World();
	
	/*
	Build Scene Stuff
	*/
	
	chooseSceneManager();
	createCamera();
	createViewports();
	
	// Set default mipmap level (NB some APIs ignore this)
	TextureManager::getSingleton().setDefaultNumMipmaps(5);
	
	// Load resources
	loadResources();

	// Create the scene
	createScene();
	createFrameListener();
}

void PlayState::exit()
{
	//
	mSceneMgr->clearScene(); 
	mSceneMgr->destroyAllCameras(); 
	mRoot->getAutoCreatedWindow()->removeAllViewports();
}

void PlayState::pause()
{
	//
}


void PlayState::resume()
{
	
}


void PlayState::loadResources()
{
	// Initialise, parse scripts etc
	ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}
void PlayState::chooseSceneManager(void)
{
	//This gets us access to more resources groups
	ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
		
	mSceneMgr = mRoot->createSceneManager(ST_EXTERIOR_CLOSE);
}

void PlayState::createCamera()
{
	//Create the camera instance, storing it in class member
	mCamera = mSceneMgr->createCamera("PlayaCam");
	
	//Now lets position it slightly above the ground
	mCamera->setPosition(Vector3(-170, 100, 0));
	mCamera->lookAt(Vector3(0, 60, 0));
	mCamera->setNearClipDistance(5);
}

bool PlayState::canCellShade()
{
	const RenderSystemCapabilities* caps = Root::getSingleton().getRenderSystem()->getCapabilities();
	if (!caps->hasCapability(RSC_VERTEX_PROGRAM) || !(caps->hasCapability(RSC_FRAGMENT_PROGRAM)))
	{
		OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your card does not support vertex and fragment programs, so cannot "
                "run this demo. Sorry!", 
                "CelShading::createScene");
				
		return false;
	}
	return true;
}

void PlayState::createScene(void)
{
	
	//Create the sky
	mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox");
		
	//Set ambient lighting
		
	/*
	I don't want to use ambient lighting (I'd rather do specific "mood" lighting!)
	but it seems like my lights that I'm making aren't lighting up the robot as I had intended.
	Perhaps its due to its positioning (it's a bit hard to guess-timate where things are
	in this 3D coordinate system. Until I figure this out, I'll just use ambient lighting
	*/
	
	mSceneMgr->setAmbientLight(ColourValue(0.7f, 0.5f, 0.6f));
	mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE);
		
	//Add some lights
	Light* l1 = mSceneMgr->createLight("l1");
	l1->setType(Light::LT_POINT);
	l1->setPosition(Vector3(0, 150, 250));
	l1->setDiffuseColour(1.0, 0.89, 0.15);
	l1->setSpecularColour(1.0, 0.89, 0.15);
	l1->setDirection(Vector3( 0 , -1, 1));
		
	Light* l2 = mSceneMgr->createLight("l2");
	l2->setType(Light::LT_SPOTLIGHT);
	l2->setDiffuseColour(0, 0, 1.0);
	l2->setSpecularColour(0, 0, 1.0);
	l2->setDirection(-1, -1, 0);
	l2->setPosition(Vector3(300, 300, 0));
	l2->setSpotlightRange(Degree(35), Degree(50));
		
	Light* l3 = mSceneMgr->createLight("l3");
	l3->setType(Light::LT_SPOTLIGHT);
	l3->setDiffuseColour(0.82, 0.91, 1.0);
	l3->setSpecularColour(0.82, 0.91, 1.0);
	l3->setDirection(0.1, -0.5, 1);
	l3->setPosition(Vector3(0, 250, -300));
	l3->setSpotlightRange(Degree(16), Degree(75));
		
	Light* l4 = mSceneMgr->createLight("l4");
	l4->setType(Light::LT_SPOTLIGHT);
	l4->setDiffuseColour(0.82, 0.91, 1.0);
	l4->setSpecularColour(0.82, 0.91, 1.0);
	l4->setDirection(0.1, -0.5, 1);
	l4->setPosition(Vector3(15, 100, -120));
	l4->setSpotlightRange(Degree(35), Degree(105));
		
	Light* l5 = mSceneMgr->createLight("l5");
	l5->setType(Light::LT_POINT);
	l5->setDiffuseColour(0, 1, 0);
	l5->setSpecularColour(0, 1, 0);
	//l5->setDirection(-1, -1, 0);
	l5->setPosition(Vector3(15, 15, 15));
	l5->setVisible(false);
	//l5->setSpotlightRange(Degree(35), Degree(50));
		
		
	//Creates terrain using a script
	//mSceneMgr->setWorldGeometry("terrain.cfg");
	
	//Create using MeshManager
	Plane plane(Vector3::UNIT_Y, 0);
	
	/*
	How can we edit the height map of a plane made with the MeshManager?
	*/
	
	
	MeshManager::getSingleton().createPlane("ground",
		ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,
		1500, 1500, 20,20, true, 1, 5, 5, Vector3::UNIT_Z);
	Entity* ground = mSceneMgr->createEntity("GroundEntity","ground");
	SceneNode* groundNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	groundNode->attachObject(ground);
	ground->setMaterialName("Examples/Rockwall"); //is this loaded in dynamically from the zip?
	ground->setCastShadows(false);
	
	Ogre::Vector3 siz(5, 2.5, 2.5);
	groundNode->setScale(siz);
	
	//Now lets attach some physics to the ground
	//OgreNewt::Collision* col = new OgreNewt::CollisionPrimitives::Cylinder(mWorld, 2.5, 5);
	OgreNewt::Collision* col = new OgreNewt::CollisionPrimitives::TreeCollision( mWorld, groundNode, false );
	OgreNewt::Body* bod = new OgreNewt::Body(mWorld, col);
	delete col;
	bod->attachToNode(groundNode);
	bod->setPositionOrientation(Ogre::Vector3(0.0, -10.0, 0.0), Ogre::Quaternion::IDENTITY);
		
	//Create Athene
	Entity* athene = mSceneMgr->createEntity("Athene","athene.mesh");
	athene->setCastShadows(true);
	SceneNode* atheneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	atheneNode->attachObject(athene);
	atheneNode->setPosition(300, 100, 300);
		
	Entity *ogre = mSceneMgr->createEntity("head", "ogrehead.mesh");
	//Do some cell shading
	if(canCellShade())
	{
		SubEntity* sub;
		// eyes
		sub = ogre->getSubEntity(0);
		sub->setMaterialName("Examples/CelShading");
		sub->setCustomParameter(CUSTOM_SHININESS, Vector4(35.0f, 0.0f, 0.0f, 0.0f));
		sub->setCustomParameter(CUSTOM_DIFFUSE, Vector4(1.0f, 0.3f, 0.3f, 1.0f));
		sub->setCustomParameter(CUSTOM_SPECULAR, Vector4(1.0f, 0.6f, 0.6f, 1.0f));
		// skin
		sub = ogre->getSubEntity(1);
		sub->setMaterialName("Examples/CelShading");
		sub->setCustomParameter(CUSTOM_SHININESS, Vector4(10.0f, 0.0f, 0.0f, 0.0f));
		sub->setCustomParameter(CUSTOM_DIFFUSE, Vector4(0.0f, 0.5f, 0.0f, 1.0f));
		sub->setCustomParameter(CUSTOM_SPECULAR, Vector4(0.3f, 0.5f, 0.3f, 1.0f));
		// earring
		sub = ogre->getSubEntity(2);
		sub->setMaterialName("Examples/CelShading");
		sub->setCustomParameter(CUSTOM_SHININESS, Vector4(25.0f, 0.0f, 0.0f, 0.0f));
		sub->setCustomParameter(CUSTOM_DIFFUSE, Vector4(1.0f, 1.0f, 0.0f, 1.0f));
		sub->setCustomParameter(CUSTOM_SPECULAR, Vector4(1.0f, 1.0f, 0.7f, 1.0f));
		// teeth
		sub = ogre->getSubEntity(3);
		sub->setMaterialName("Examples/CelShading");
		sub->setCustomParameter(CUSTOM_SHININESS, Vector4(20.0f, 0.0f, 0.0f, 0.0f));
		sub->setCustomParameter(CUSTOM_DIFFUSE, Vector4(1.0f, 1.0f, 0.7f, 1.0f));
		sub->setCustomParameter(CUSTOM_SPECULAR, Vector4(1.0f, 1.0f, 1.0f, 1.0f));
		ogre->setCastShadows(true);
		mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ogre);
		ogre->getParentSceneNode()->setPosition(100, 100, 100);
	}
	
	//Create ninja
	Entity *ninja = mSceneMgr->createEntity("Ninja","robot.mesh");
	ninja->setCastShadows(true);
	ninja->setNormaliseNormals(true);
	SceneNode* ninjaNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	ninjaNode->attachObject(ninja);
	ninjaNode->attachObject(mCamera);
	
	//Make it a bit smaller...ninja friggin huge
	//ninjaNode->setScale(0.5f, 0.5f, 0.5f);
	ninjaNode->attachObject(l4);
	//Setup the walk list
	//mWalkList.push_back(Vector3(550.0f, 0.0f, 50.0f));
	mWalkList.push_back(Vector3(1.0f, 0.0f, 0.0f));
	//mWalkList.push_back(Vector3(-100.0f, 0.0f, -200.0f));
	
	//Give character physics
	
	//Watch out --- messing with these params causes him to rotate!
	col = new OgreNewt::CollisionPrimitives::Cylinder(mWorld, 1, 1);
	bod = new OgreNewt::Body(mWorld, col);
	delete col;
	Ogre::Vector3 inertia = OgreNewt::MomentOfInertia::CalcBoxSolid(10.0, Vector3(2.0f, 2.0f, 2.0f));
	bod->setMassMatrix( 10.0, inertia );

	// attach to the scene node.
	bod->attachToNode( ninjaNode );

	// this is a standard callback that simply add a gravitational force (-9.8*mass) to the body.
	bod->setStandardForceCallback();

	// set the initial orientation and velocity!
	Ogre::Quaternion playerOrientation = ninjaNode->getWorldOrientation();
	Ogre::Vector3 pos = ninjaNode->getWorldPosition();
	Ogre::Vector3 dir, vec;
	vec = Ogre::Vector3(0,0,-1);
	dir = playerOrientation * vec;
	bod->setPositionOrientation( pos, playerOrientation );
	bod->setVelocity( (dir * 0.0) );
	
	//Now set the class level Entity and SceneNode pointers to use these
	mEntity = ninja;
	mNode = ninjaNode;
	
	//Set up camera track
	
	//Create camera node
	
	camNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	//set up spline animation of node
	Animation* anim = mSceneMgr->createAnimation("CameraTrack", 10);
	// spline it for nice curves
	anim->setInterpolationMode(Animation::IM_SPLINE);
	// create a track to animate the camera's node
	NodeAnimationTrack* track = anim->createNodeTrack(0, camNode);
	// setup keyframes
	TransformKeyFrame* key = track->createNodeKeyFrame(0); // startposition
	key = track->createNodeKeyFrame(2.5);
	key->setTranslate(Vector3(500,500,-1000));
	key = track->createNodeKeyFrame(5);
	key->setTranslate(Vector3(-1500,1000,-600));
	key = track->createNodeKeyFrame(7.5);
	key->setTranslate(Vector3(0,-100,0));
	key = track->createNodeKeyFrame(10);
	key->setTranslate(Vector3(0,0,0));
	// create a new animation state to track this
	mCamAnimState = mSceneMgr->createAnimationState("CameraTrack");
	mCamAnimState->setLoop(true);
	mCamAnimState->setEnabled(true);
	
	
	/*
	OverlayManager& overlayManager = OverlayManager::getSingleton();

// Create a panel
OverlayContainer* panel = static_cast<OverlayContainer*>(
    overlayManager.createOverlayElement("Panel", "PanelName"));
panel->setMetricsMode(Ogre::GMM_PIXELS);
panel->setPosition(10, 10);
panel->setDimensions(100, 100);
//panel->setMaterialName("MaterialName"); // Optional background material

// Create a text area
TextAreaOverlayElement* textArea = static_cast<TextAreaOverlayElement*>(
    overlayManager.createOverlayElement("TextArea", "TextAreaName"));
textArea->setMetricsMode(Ogre::GMM_PIXELS);
textArea->setPosition(0, 0);
textArea->setDimensions(100, 100);
textArea->setCaption("Hello, World!");
textArea->setCharHeight(16);
//textArea->setFontName("bluehigh.ttf");
textArea->setColourBottom(ColourValue(0.3, 0.5, 0.3));
textArea->setColourTop(ColourValue(0.5, 0.7, 0.5));

// Create an overlay, and add the panel
Overlay* overlay = overlayManager.create("OverlayName");
overlay->add2D(panel);

// Add the text area to the panel
panel->addChild(textArea);

// Show the overlay
overlay->show();
*/
}

void PlayState::createViewports()
{
	// Create one viewport, entire window
	Viewport* vp = mWindow->addViewport(mCamera);
	vp->setBackgroundColour(ColourValue(0,0,0));

	// Alter the camera aspect ratio to match the viewport
	mCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
}

void PlayState::createFrameListener(void)
{
	//
	mFrameListener = new NVFrameListener(mWindow, mCamera, mNode, mEntity, mWalkList);
	//( (NVFrameListener*)mFrameListener)->setApplication(this); commented out for this setup
	( (NVFrameListener*)mFrameListener)->setSceneManager(mSceneMgr);
	( (NVFrameListener*)mFrameListener)->setNewtonWorld(mWorld);
	mFrameListener->showDebugOverlay(false);
	mRoot->addFrameListener(mFrameListener);
	
	// this is a basic frame listener included with OgreNewt that does nothing but update the
	// physics at a set framerate for you.  complex project will want more control, but this
	// works for simple demos like this.  feel free to look at the source to see how it works.
	mNewtonListener = new OgreNewt::BasicFrameListener( mWindow, mSceneMgr, mWorld, 120 );
	mRoot->addFrameListener(mNewtonListener);
}

SoundEngine* PlayState::getSoundEngine()
{
	return mSoundEngine;
}